/** uupaa-detect.js
 *
 * uupaa-detect.js is detect User-Agent and Functions
 *  - uupaa.js spin-off project
 *
 * @author Takao Obara <uupaa.js@gmail.com>
 * @license uupaa-detect.js is licensed under the terms and conditions of the MIT licence.
 * @version 1.1
 * @date 2008-12-19
 * @see <a href="http://code.google.com/p/uupaa-js/">uupaa.js Home(Google Code)</a>
 * @see <a href="http://code.google.com/p/uupaa-js-spinoff/">uupaa.js SpinOff Project Home(Google Code)</a>
 */

if (!window.uu) {

// --- core ---
var uu;
window.uu = function() { return uu._impl.apply(this, arguments); }; // adapter
window.uu.mix = function(base, flavor, aroma) {
  for (var i in flavor) { base[i] = flavor[i]; }
  return aroma ? uu.mix(base, aroma) : base;
};
window.uu.mix.param = function(base, flavor, aroma) {
  for (var i in flavor) { !(i in base) && (base[i] = flavor[i]); }
  return aroma ? uu.mix.param(base, aroma) : base;
};
window.uu._uuid = 0; // unique id counter
window.uu.uuid = function() { return ++window.uu._uuid; };
window.uuClass = {};
window.uuConst = {};
window.uuConfig = {
  Core: { debug: 0 }
};

// --- local scope ------------------------------------------------------
(function() {

// --- The "OOP Class" ---
// uuClass - create a simple class
uuClass = _ClassKISS;

// uuClass.Generic - create a generic class
uuClass.Generic = _ClassGeneric;

// uuClass.Singleton - create a singleton class
uuClass.Singleton = _ClassSingleton;

// --- URL manipulator ---
// uu.url - parse and build URL
uu.url = _URL;

// uu.url.toAbsURL - to absolute URL
uu.url.toAbsURL = _URLtoAbsURL;

// uu.url.query - parse and build QueryString
uu.url.query = _URLQuery;

/** detect User-Agent and Functions
 *
 * @class
 */
uuClass.Detect = uuClass();

var _win = window, _doc = document, n = navigator.userAgent,
    _ua = uuClass.Detect,
    _cmpt = _doc.compatMode, _impl = _doc.implementation, v = 0,
    CONSTRUCTOR = "construct",
    DESTRUCTOR  = "destruct",
    STABLED     = "stabled",
    MESSAGEBOX  = "msgbox";

_ua.ie = _ua.gecko = _ua.webkit = _ua.opera = _ua.air = _ua.ipod = _ua.wii = false;
_doc.uniqueID      ? (_ua.ie     = true) :    // is IE
/Gecko\//.test(n)  ? (_ua.gecko  = true) :    // is Gecko(Firefox, Camino)
/WebKit/.test(n)   ? (_ua.webkit = true) :    // is WebKit(Safari, Konqueror, Chrome)
_win.opera         ? (_ua.opera  = true) :    // is Opera(Opera, Opera Mini, Wii)
/AdobeAIR/.test(n) ? (_ua.air    = true) : 0; // is Adobe AIR
v = parseFloat(_ua.ie     ? n.match(/MSIE ([\d]\.[\d][\w]?)/)[1]  : // IE Major version
               _ua.gecko  ? n.match(/Gecko\/(\d{8})/)[1]          : // Gecko Build Number
               _ua.webkit ? n.match(/WebKit\/(\d+(?:\.\d+)*)/)[1] : // Webkit Build Number
               _ua.opera  ? opera.version() : 0);                   // Opera browser version
_ua.webkit && /iPod|iPhone/.test(n) && ++_ua.ipod;          // is iPod/iPhone(Safari)
_ua.opera  && _win.opera.wiiremote && ++_ua.wii;            // is Wii Internet channel
_ua.ie8       = _ua.ie && v >= 8;                           // is IE8+
_ua.ie8mode8  = _ua.ie8 && _doc.documentMode >= 8;          // is IE8+(in IE8 and edge mode)
_ua.ie7       = _ua.ie && v === 7;                          // is IE7
_ua.ie6       = _ua.ie && v === 6;                          // is IE6
_ua.fx40      = _ua.gecko && /fox\/4\.[\d]/.test(n);        // is Firefox4.x+
_ua.fx31      = _ua.gecko && /fox\/3\.([1-9])/.test(n);     // is Firefox3.1+
_ua.fx30      = _ua.gecko && /fox\/3\.0/.test(n);           // is Firefox3.0
_ua.fx20      = _ua.gecko && /fox\/2\.0/.test(n);           // is Firefox2.0
_ua.chrome    = _ua.webkit && /Chrome\//.test(n);           // is Google Chrome(V8)
_ua.safari3   = _ua.webkit && !_ua.chrome && v > 500        // is Safari3
                           && /Safari\//.test(n);
_ua.opera100  = _ua.opera && v >= 10;                       // is Opera10.x+
_ua.opera95   = _ua.opera && v >= 9.5 && v < 10;            // is Opera9.5x, 9.6x
_ua.opera90   = _ua.opera && v >= 9.0 && v < 9.5;           // is Opera9.0x, 9.1x, 9.2x
_ua.std       = _cmpt && _cmpt === "CSS1Compat";            // is Standard Mode, (false = Quirks Mode)
_ua.selector  = !!(_doc.querySelector && !_ua.ie);          // is Selectors API, Firefox3.1+, Safari3+, Chrome, !IE8b2(buggy)
_ua.range     = _impl && _impl.hasFeature("Range", "2.0");  // is DOM Level2 Range Module
_ua.etraverse = _ua.fx31 || _ua.opera95;                    // is DOM Element Traversal Module, Firefox3.1+, Opera9.5x, !Opera9.2x(buggy)
_ua.minquartz = (_ua.gecko || _ua.chrome) ? 10 : 16;        // minimum quartz(unit: ms)[truth of chrome is 1ms]
_ua.disptable = !(_ua.ie6 || _ua.ie7);                      // display:table support(!IE6, !IE7)
_ua.docroot   = _doc.documentElement ||                     // document root element(document.documentElement)
                _doc.getElementsByTagName("html")[0];
_ua.version   = v;
_ua.base      = _URLBase();

// --- Array.prototype Cross Browser ---
(function(p){
  p.lastIndexOf||(p.lastIndexOf=function(needle,fromIndex){var iz=this.length,i=fromIndex;i=(i<0)?i+iz:iz-1;for(;i>-1;--i){if(i in this&&this[i]===needle){return i;}}return -1;});
  p.indexOf||(p.indexOf=function(needle,fromIndex){var iz=this.length,i=fromIndex||0;i=(i<0)?i+iz:i;for(;i<iz;++i){if(i in this&&this[i]===needle){return i;}}return -1;});
  p.forEach||(p.forEach=function(fn,me){var i=0,iz=this.length;for(;i<iz;++i){if(i in this){fn.call(me,this[i],i,this);}}});
  p.filter||(p.filter=function(fn,me){var rv=[],ri=-1,i=0,iz=this.length,v;for(;i<iz;++i){if(i in this){v=this[i];if(fn.call(me,v,i,this)){rv[++ri]=v;}}}return rv;});
  p.every||(p.every=function(fn,me){var i=0,iz=this.length;for(;i<iz;++i){if(i in this){if(!fn.call(me,this[i],i,this)){return false;}}}return true;});
  p.some||(p.some=function(fn,me){var i=0,iz=this.length;for(;i<iz;++i){if(i in this){if(fn.call(me,this[i],i,this)){return true;}}}return false;});
  p.map||(p.map=function(fn,me){var rv=Array(this.length),i=0,iz=this.length;for(;i<iz;++i){if(i in this){rv[i]=fn.call(me,this[i],i,this);}}return rv;});
  p.reduce||(p.reduce=function(fn,initialValue){var rv,i=0,iz=this.length,found=0;if(initialValue!==void 0){rv=initialValue;++found;}for(;i<iz;++i){if(i in this){if(found){rv=fn(rv,this[i],i,this);}else{rv=this[i];++found;}}}if(!found){throw TypeError();}return rv;});
  p.reduceRight||(p.reduceRight=function(fn,initialValue){var rv,i=0,found=0;if(initialValue!==void 0){rv=initialValue;++found;}for(i=this.length-1;i>=0;--i){if(i in this){if(found){rv=fn(rv,this[i],i,this);}else{rv=this[i];++found;}}}if(!found){throw TypeError();}return rv;});
})(Array.prototype);

// --- String.prototype Cross Browser ---
(function(p){
  p.trim||(p.trim=function(){return this.replace(/^[\s]+|[\s]+$/g,"");}); // from Firefox3.1
  p.trimLeft||(p.trimLeft=function(){return this.replace(/^[\s]+/g,"");}); // from Firefox3.1
  p.trimRight||(p.trimRight=function(){return this.replace(/[\s]+$/g,"");}); // from Firefox3.1
})(String.prototype);

// --- Math Cross Browser ---
(function(p){
  p.toDegrees||(p.toDegrees=180/Math.PI); // to degree. from java.math
  p.toRadians||(p.toRadians=Math.PI/180); // to radian. from java.math
})(Math);

// --- HTMLElement.prototype Cross Browser ---
!document.uniqueID&&!HTMLElement.prototype.outerHTML&&(function(p){
  p.__defineGetter__("outerHTML",function(){var range=document.createRange(),div=document.createElement("div");range.selectNode(this);div.appendChild(range.cloneContents());return div.innerHTML;});
  p.__defineSetter__("outerHTML",function(html){var range=document.createRange(),cf;range.setStartBefore(this);cf=range.createContextualFragment(html);this.parentNode.replaceChild(cf,this);});
  p.__defineGetter__("innerText",function(){return this.textContent;});
  p.__defineSetter__("innerText",function(text){while(this.hasChildNodes()){this.removeChild(this.lastChild);}this.appendChild(document.createTextNode(text));});
})(HTMLElement.prototype);

// --- URL ---
function _URL(url /* = "." */) {
  if (url === void 0 || typeof url === "string") {
    // parse URL
    var x = _URLtoAbsURL(url || "."), m, dir = "", file = "", ary, hash;
    if ( (m = x.match(/^file:\/\/(?:\/)?(?:localhost\/)?((?:[a-z]\:)?.*)$/)) ) {
      ary  = m[1].split("/");
      file = ary.pop();
      dir  = ary.join("/");
      return { url: x, scheme: "file", domain: "", port: "",
               base: "file:///" + dir || "/", path: m[1],
               dir: dir || "/", file: file || "",
               query: "", hash: {}, fragment: "" };
    } else if ( (m = x.match(/^([a-z][a-z\d\+\-\.]*)\:\/\/([^\/\:]+)(?:\:(\d*))?([^\?#\s]*)(?:\?([^#]*))?(?:#(.*))?/i)) ) {
      if (m[4]) {
        ary  = m[4].split("/");
        file = ary.pop();
        dir  = ary.join("/");
      }
      hash = m[5] ? _URLQuery(m[5]) : {};
      return {
        // "http://wwww.example.com:8080/dir/file.ext?key1=value1&key2=value2#menu1"
        url:      x,            // AbsoluteURLString( "http://..." )
        scheme:   m[1],         // SchemeString( "file" or "http" or "https" )
        domain:   m[2],         // DomainNameString( "www.example.com" )
        port:     m[3] || "",   // PortNumber( "8080" or "" ) - not Number
        base:     (m[1] + "://" + m[2]) + (m[3] ? ":" + m[3] : "") + (dir || "/"),
        path:     m[4] || "/",  // PathString( "/dir/file.ext" or "/" )
        dir:      dir  || "/",  // DirString( "/dir" or "/" )
        file:     file || "",   // FileNameString( "file.ext" or "" )
        query:    m[5] || "",   // QueryString( "key1=value1&key2=value2" or "" )
        hash:     hash,         // QueryHash( { key1: "value1", key2: "value2" } )
        fragment: m[6] || ""    // FragmentString( "menu1" or "" )
      };
    }
    return false;
  }
  // build URL
  return [url.scheme, "://",
          url.domain,
          url.port ? ":" + url.port : "",
          url.path || "/",
          url.query ? "?" + url.query : "",
          url.fragment ? "#" + url.fragment : ""].join("");
}

function _URLtoAbsURL(url /* = "." */) {
  url = url || ".";
  if (/^(file|https|http)\:\/\//.test(url)) {
    return url;
  }
  var e = _doc.createElement("div");
  e.innerHTML = '<a href="' + url + '" />';
  return e.firstChild.href;
}

function _URLQuery(query) {
  var rv, fn, i;
  if (typeof query === "string") {
    // parse QueryString
    rv = {}, fn = decodeURIComponent;
    query.replace(/^.*\?/, "").replace(/&amp;/g, "&").
          replace(/(?:([^\=]+)\=([^\&]+)&?)/g, function(_, key, value) {
      return rv[fn(key)] = fn(value);
    });
    return rv;
  }
  // build QueryString
  rv = [], fn = encodeURIComponent;
  for (i in query) {
    rv.push(fn(i) + "=" + fn(query[i]));
  }
  return rv.join("&");
}

function _URLBase() {
  var core = _doc.getElementById("uupaa.js"), hash, tags, v, i, iz,
      loc = location;
  if (!core) {
    tags = _doc.getElementsByTagName("script");
    for (i = 0, iz = tags.length; i < iz; ++i) {
      v = tags[i];
      if (/uupaa.*\.js$/.test(v.src) || /uupaa-detect.*\.js$/.test(v.src)) {
        core = v;
        break;
      }
    }
  }
  hash = uu.url(core ? core.src
                     : loc.protocol + "//" + loc.pathname.replace(/\\/g, "/"));
  return hash.base;
}

function _ClassKISS(/* args, ... */) {
  // when "this" is referred in this scope, "this" is equal to window
  return function() {
    // when "this" is referred in this scope, "this" shows the instance newly generated
    this[CONSTRUCTOR] && this[CONSTRUCTOR].apply(this, arguments);
  };
}

function _ClassGeneric(/* args, ... */) {
  return function() {
    !("uuid" in this) && (this.uuid = uu.uuid()); // add "uuid" property
    this[MESSAGEBOX] && uu.msg && uu.msg.regist(this);
    this[CONSTRUCTOR] && this[CONSTRUCTOR].apply(this, arguments);
    if (this[DESTRUCTOR]) {
      var me = this, dest = function() { me[DESTRUCTOR](); };
      if (_ua.ie) {
        _win.attachEvent("onunload", dest);
      } else {
        _win.addEventListener("unload", dest, false);
      }
    }
  };
}

function _ClassSingleton(/* args, ... */) {
  return function() {
    var me = this, a = arguments, fn = a.callee, dest;
    if (!fn.instance) {
      fn.instance = me; // keep instance
      !("uuid" in me) && (me.uuid = uu.uuid()); // add "uuid" property
      me[MESSAGEBOX] && uu.msg && uu.msg.regist(me);
      me[CONSTRUCTOR] && me[CONSTRUCTOR].apply(me, a);
      if (me[DESTRUCTOR]) {
        dest = function() { me[DESTRUCTOR](); };
        if (_ua.ie) {
          _win.attachEvent("onunload", dest);
        } else {
          _win.addEventListener("unload", dest, false);
        }
      }
    } else {
      me[STABLED] && me[STABLED].apply(me, a); // after the second
    }
    return fn.instance; // fn.instance is returned. "this" is abandoned.
  };
}

})(); // end (function())() scope

} // if (!window.uu)

